#Used to save network inforamtion and for network reshaping and equivilance testing.
require 'NetworkCommunicator'
require 'PeerTable'
require 'PeerRecord'
require 'Message'
class Peer < NetworkCommunicator #TODO: rename to peer *DONE*
  #@peer_table < the peer table to use with networkmanagment
  #@port < the port to have open TODO: allow this to be nil for no port
  #@address < the network address to listen on TODO: allow this to be nil for no address
  #@name < the default name of the peer TODO: change to a name array?
  #@network_communicator < the network_communicator to use to interface with the network
  #@message_received_listeners < the lambdas that are executed when a message is received. @TODO: change to private and add a getter and setter
  attr_accessor :peer_table, :network_communicator, :auto_accept_names, :auto_connect_ip, :auto_accept_ip,
  #the constructor
  #set the default value for port to 2035
  #set the default value for name to "peer-[randomnumber]"
  #set the default listen address to all
  #set the default peer table to nil  
  def initialize(port=DEFAULT_PORT, name="peer-#{rand(10000)}", listen_address="*",
      p_table = nil, auto_accept_name = false, auto_connect_ip = false, auto_accept_ip = true)
    @partial_peers = Array.new
    @auto_accept_name = auto_accept_name
    @auto_connect_ip = auto_connect_ip
    @auto_accept_ip = auto_accept_ip
    @gourami_network_listeners = Array.new
    @port = port
    @name = name
    @address = listen_address
    @peer_table = p_table
    super(port,listen_address)
    #sets up labdas
    @message_received = lambda do |mes|
      process_gourami_message(mes) if mes.protocol == "GOURAMI" #all GOURAMI messages are dealt with here first
    end
    #adds the lambdas to their respective places
    self.message_received_listeners.push(@message_received)
  end
  def set_challange_pair(key1, key2)
    @challange_pairs.push("#{key1}:#{key2}")
  end
  #the challange pair getter
  def get_challange_pair(key1)
    for key in @challange_keys
      return key.split(':')[1] if key.split(':')[0] == key1
    end
  end
  #TODO:assess whether to use the constructor to set values or to use the start method
  #this deals with capturing GOURAMI messages and dealing with them
  def start_peer
      Thread.new do
        #TODO: these sections need to be tidied up (including the constructor)
        self.audit = true
        self.start
        puts self.message_received_listeners[0]
      end
    #TODO else raise some error.
  end
  def load_peer_table(peer_table_file,peer_table_size=-1)
    @peer_table = PeerTable.new(peer_table_file,peer_table_size)
    for p in @peer_table.peers
      self.connect(p.ip,p.port)
    end
  end
  #connect to a peer from the peer table or from a specified ip / port
  #TODO: access the possible impact of two peers with the same name in the peer table
  #TODO: allow auto_register pass through
  def connect_to_peer(ip=nil, port=nil, name=nil, trust_peer_table=true)
    #if the name is not nil
    if name != nil
      #go through every peer
      for peer in @peer_table.peers
        #if there is a match in names
        if peer.name == name
          #check to see if there is no ip and port specified or they are the same
          #TODO consider chaning NetworkCOmmunicator.connect to accept a block to execute the name registration?
          if (peer.ip == ip && peer.port = port) || (ip == nil && port == nil)
            #there is no discrepancy between arg values and the table_values
            self.connect(peer.ip, peer.port)
          elsif (trust_peer_table)
            #there is a discrepancy but trust is placed in the table (default behaviour)
            self.connect(peer.ip, peer.port)
          else
            #there is a discrepancy but trust is placed in the supplied arg values
            self.connect(ip,port)
            #the table values are updated to reflect the last trusted assertions
            peer.ip = ip
            peer.port = port
          end
        end
      end
    else
      #connect to the specified port and ip if and only if they are both specified
      @network_communicator.connect(ip, port) if (port != nil && ip != nil)
    end
  end
  
  def process_gourami_message(message)
    case message.message_text.split(':')[0]
    when "SET NAME"
      if message.message_text.split(':').length > 1
        reg_name = true if message.sending_connection.name == ""
        message.sending_connection.name = message.message_text.split(':')[1] if message.sender_path.length < 2 && message.message_text.split(':')[1].length > 0
        Message.new("#{message.sending_connection.name}[GOURAMI]{SET NAME:#{@name}}") if reg_name == true
      end
    when "RELEASE IP"
      if message.message_text.split(':').length > 1
        message.sending_connection.release_ip = (message.message_text.split(':')[1] == "TRUE" ? true : false) if message.sender_path.length < 2
      end
    when "GET IP"
      if message.message_text.split(':').length > 1
        for c in Connection.connections
          if c.name == message.message_text.split(':')[1]
            if c.release_ip == true
              message.return_message("IP:#{c.ip}")
            else
              message.return_message("IP:#{message.message_text.split(':')[1]}:#{c.name}:NOT AVAILABLE")
            end
          end
        end
      end
    when "LIST NAMES"
      userlist = ""
      for c in Connection.connections
        userlist += c.name + ":"
      end
      #manual creation
      message.return_message("NAMES:#{userlist}")
      #message.return_message("USERS:#{userlist}")
    when "SET CHALLANGE PAIR"
      if message.message_text.split(':').length > 2
        message.sending_connection.set_challange_pair(message.message_text.split(':')[1], message.message_text.split(':')[2])
      end
    when "GET CHALLANGE PAIR"
      if message.message_text.split(':').length > 1
        key2 = c.get_challange_pair(message.message_text.split(':')[1]) if key2 != nil
        message.return_message("CHALLANGE:#{message.message_text.split(':')[1]}:#{key2 != nil ? key2 : 'NO PAIR'}")
      end
    when "GET CONNECT PORT"
      message.return_message("CONNECT PORT:#{port == nil ? 'CONNECT PORT NOT AVAILABLE' : port.to_s}")
    when "IP"
      found = false
      for p in @partial_peers
        if p.path == message.sending_connection.name + '/' + message.sender_path.join('/') + message.message_text.split(':')[1]
          found = true
          p.ip = message.message_text.split(':')[2]
          if p.port != nil
            PeerTable.add_peer(p.ip, p.port, message.sender_path[-1]) if p.port != nil
          else
            Message.send_message(message.sending_connection.name + '/' + message.sender_path.join('/') + message.message_text.split(':')[1], "GET CONNECT PORT", "GOURAMI", true)
          end
        end
      end
      if found == false
        p = PeerRecord.new()
        p.ip = message.message_text.split(':')[2]
        @partial_peers.push p
      end
    when "CONNECT PORT"
      found = false
      for p in @partial_peers
        if p.path == message.sending_connection.name + '/' + message.sender_path.join('/')
          found = true
          p.port = message.message_text.split(':')[1]
          if p.ip != nil
            PeerTable.add_peer(p.ip, p.port, message.message_text.split(':')[1])
          else
            Message.send_message(message.sending_connection.name + '/' + message.sender_path.join('/')[0..], "GET GET IP", "GOURAMI", true)
          end
        else
          
        end
      end
      if found == false
        p = PeerRecord.new()
        p.port = message.message_text.split(':')[1]
        @partial_peers.push p
      end
  end
end
